package chapter5;

import lombok.extern.slf4j.Slf4j;

import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author caiya
 * @date 2020/3/26 21:59
 */
@Slf4j
public class LinkedBlockingQueueTest {
    static final LinkedBlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue(5);

    public static void main(String[] args) throws InterruptedException {
        /**
         * LinkedBlockingQueue：单向可指定大小的阻塞队列，基于单向链表
         *      底层使用独占锁保证操作原子性，而非使用 cas，使用锁就会导致线程被阻塞挂起，所以 BlockingQueue 是阻塞队列
         * 1、boolean offer(E e)：非阻塞插入
         *      1）队尾插入
         *      2）队列满时插入失败，返回 false
         *      3）e 不允许为 null，否则 NPE
         * 2、void put(E e)：阻塞插入
         *      1）队尾插入
         *      2）队列满时，阻塞当前线程（condition.await），直到队列有空闲插入成功后返回
         *      3）可能会抛出 InterruptedException 异常（步骤2被阻塞时可能会被别的线程interrupt）
         *      4）e 不允许为 null，否则 NPE
         * 3、E poll()：非阻塞获取并移除
         *      1）队首获取并移除元素
         *      2）如果队列为空，则返回 null
         * 4、E peek()：非阻塞获取但不移除
         *      1）队首获取但是不移除元素
         *      2）如果队列为空，则返回 null
         * 5、E take()：阻塞获取并移除
         *      1）队首获取并移除元素
         *      2）队列为空时，阻塞当前线程（condition.await），直到队列不为空然后获取元素并删除
         *      3）可能会抛出 InterruptedException 异常（步骤2被阻塞时可能会被interrupt）
         * 6、boolean remove(Object o)：
         *      1）有则删除并返回 true
         *      2）没有则返回 false
         *      3）该操作是线程安全的
         *      4）非阻塞
         * 7、int size()：获取队列大小（准确，放心使用）
         * 8、boolean contains(Object o)：判断队列是否包含指定元素（准确，放心使用）
         */

        log.debug("linkedBlockingQueue.size(): {}", linkedBlockingQueue.size());
        log.debug("linkedBlockingQueue.poll(): {}", linkedBlockingQueue.poll());

        linkedBlockingQueue.offer("张三");
        linkedBlockingQueue.offer("王五");
        linkedBlockingQueue.offer("李四");
        linkedBlockingQueue.offer("王五");

        log.debug("linkedBlockingQueue.size(): {}", linkedBlockingQueue.size());

        // poll获取队首元素后，size-1
        log.debug("linkedBlockingQueue.poll(): {}", linkedBlockingQueue.poll());
        log.debug("linkedBlockingQueue.size(): {}", linkedBlockingQueue.size());

        // peek获取队首元素后，size不变
        log.debug("linkedBlockingQueue.peek(): {}", linkedBlockingQueue.peek());
        log.debug("linkedBlockingQueue.size(): {}", linkedBlockingQueue.size());

        // 删除的元素有很多时，只会删除排在最前面的那个，并返回true
        log.debug("linkedBlockingQueue.remove(): {}", linkedBlockingQueue.remove("法斯特"));
        log.debug("linkedBlockingQueue.remove(): {}", linkedBlockingQueue.remove("王五"));
        log.debug("linkedBlockingQueue.size(): {}", linkedBlockingQueue.size());

        // remove只删除了一个 "王五"，所以contains返回true
        log.debug("linkedBlockingQueue.contains(): {}", linkedBlockingQueue.contains("王五"));

        Iterator<String> iterator = linkedBlockingQueue.iterator();
        while (iterator.hasNext()) {
            String e = iterator.next();
            log.debug(e);
        }

        linkedBlockingQueue.put("王五");
        linkedBlockingQueue.put("王五");
        linkedBlockingQueue.put("王五");

        new Thread(() -> {
            try {
                Thread.sleep(3000);
                linkedBlockingQueue.poll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        // 队列大小为5，再插入就会阻塞挂起到当前位置，直到队列可以继续 put，再返回从阻塞的地方继续执行
        linkedBlockingQueue.put("王五");

        log.debug("【3s后打印】linkedBlockingQueue.size(): {}", linkedBlockingQueue.size());
    }
}
